package com.jgy.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jgy.entity.XmcData;
import com.jgy.event.PublishEventVo;
import com.jgy.service.XmcDataService;
import com.jgy.mapper.XmcDataMapper;
import com.jgy.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.RedissonMapCache;
import org.redisson.api.RLock;
import org.redisson.api.RMapCache;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
* @author light
* @description 针对表【xmc_data】的数据库操作Service实现
* @createDate 2023-03-03 10:30:59
*/
@Service("xmcDataService")
@Slf4j
public class XmcDataServiceImpl extends ServiceImpl<XmcDataMapper, XmcData>
    implements XmcDataService{

    @Resource
    private ApplicationContext applicationContext;
    @Autowired
    private XmcDataMapper mapper;
    @Autowired
    RedissonClient redissonClient;
    @Resource
    private RedisTemplate<String,Object> redisTemplate;

    private static final String DEFAULT_DETA = "light_";

    /**
     * redisson分布式锁
     * @param tel
     * @return
     */
    @Override
    public XmcData selectList(String tel) {


        //从redis中读取数据
        String jsonString = (String) redisTemplate.opsForValue().get(DEFAULT_DETA+tel);

        if (StringUtils.isNotEmpty(jsonString)){

            /**
             * 缓存穿透 -- 通过查询某个不存在的id而从可以一直访问数据库导致数据库宕机，
             * 解决思路就是数据库没查到数据的情况下，给redis存一个空的key，并设置过期时间
             */
            if("null".equals(jsonString)) {
                return null;
            }

            log.info("=从缓存查=--redis");
            return JSON.parseObject(jsonString, XmcData.class);
        }

        else {

            //每⻔课程设置⼀个锁
            RLock lock = redissonClient.getLock("coursequerylock:"+tel);
            lock.lock();
            try {
                 jsonString = (String) redisTemplate.opsForValue().get(DEFAULT_DETA+tel);
                if(StringUtils.isNotEmpty(jsonString)){
                    log.info("颗粒度不够细，导致还是走了数据库查询的方法，但是被捕捉到。。。");
                    return JSON.parseObject(jsonString, XmcData.class);
                }
                log.info("从数据库查询...");
                LambdaQueryWrapper<XmcData> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(XmcData::getTel,tel);
                List<XmcData> xmcData = mapper.selectList(queryWrapper);
                //存入redis
                redisTemplate.opsForValue().set(DEFAULT_DETA+tel, JSON.toJSONString(xmcData.get(0))
                        /**
                         * 设置过期时间 再加一个随机时间，防止key同时过期
                         * 解决缓存雪崩--即大量key同时过期
                         */
                        ,300 + new Random().nextInt(100)
                        , TimeUnit.SECONDS);
                return xmcData.get(0);
            } finally {
                lock.unlock();
            }
        }
    }

    /**
     * synchronized 单服务锁
     * @param tel
     * @return
     */
    @Override
    public XmcData selectList1(String tel) {


        //从redis中读取数据
        String jsonString = (String) redisTemplate.opsForValue().get(DEFAULT_DETA+tel);
        if (StringUtils.isNotEmpty(jsonString)){
            /**
             * 缓存穿透 -- 通过查询某个不存在的id而从可以一直访问数据库导致数据库宕机，
             * 解决思路就是数据库没查到数据的情况下，给redis存一个空的key，并设置过期时间
             */
            if("null".equals(jsonString)) {
                return null;
            }

            log.info("=从缓存查=--redis");
            XmcData xmcData = JSON.parseObject(jsonString, XmcData.class);
            return xmcData;
        }
        else {
            /**
             * 缓存击穿，有一个热点key，一旦过期，就会很多请求瞬间冲击数据库
             * 在请求数据库的时候价格锁，限制一个请求访问连接数据库，
             * 其他的请求走redis
             */
            synchronized (this){
                log.info("从数据库查询...");
                LambdaQueryWrapper<XmcData> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(XmcData::getTel,tel);
                List<XmcData> xmcData = mapper.selectList(queryWrapper);

                //存入redis
                redisTemplate.opsForValue().set(DEFAULT_DETA+tel, JSON.toJSONString(xmcData.get(0))
                        /**
                         * 设置过期时间 再加一个随机时间，防止key同时过期
                         * 解决缓存雪崩--即大量key同时过期
                         */
                        ,300 + new Random().nextInt(100)
                        , TimeUnit.SECONDS);
                return xmcData.get(0);
        }
        }
    }


    @Override
    public R publish() {
        PublishEventVo publishEventVo = new PublishEventVo();
        publishEventVo.setEventCreator("xxxx");
        publishEventVo.setEventCreatorEducation("000");
        applicationContext.publishEvent(publishEventVo);
        System.out.println(publishEventVo);
        System.out.println("又又又又又又又又又又又又又又又又又又又又又又又又又又");
        return R.ok(publishEventVo);
    }
}





